﻿/**************************************************************************
  exif.cpp  -- A simple ISO C++ library to parse basic EXIF
               information from a JPEG file.

  Copyright (c) 2010-2015 Mayank Lahiri
  mlahiri@gmail.com
  All rights reserved (BSD License).

  See exif.h for version history.

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

  -- Redistributions of source code must retain the above copyright notice,
     this list of conditions and the following disclaimer.
  -- Redistributions in binary form must reproduce the above copyright notice,
     this list of conditions and the following disclaimer in the documentation
     and/or other materials provided with the distribution.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESS
  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
  NO EVENT SHALL THE FREEBSD PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
  OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "exif.h"

#include <algorithm>
#include <cstdint>
#include <stdio.h>
#include <vector>

using std::string;

struct Rational {
  uint32_t numerator, denominator;
  operator double() const {
    if (denominator < 1e-20) {
      return 0;
    }
    return static_cast<double>(numerator) / static_cast<double>(denominator);
  }
};

// IF Entry
class IFEntry {
 public:
  using byte_vector = std::vector<uint8_t>;
  using ascii_vector = std::string;
  using short_vector = std::vector<uint16_t>;
  using long_vector = std::vector<uint32_t>;
  using rational_vector = std::vector<Rational>;

  IFEntry()
      : tag_(0xFF), format_(0xFF), data_(0), length_(0), val_byte_(nullptr) {}
  IFEntry(const IFEntry &) = delete;
  IFEntry &operator=(const IFEntry &) = delete;
  IFEntry(IFEntry &&other)
      : tag_(other.tag_),
        format_(other.format_),
        data_(other.data_),
        length_(other.length_),
        val_byte_(other.val_byte_) {
    other.tag_ = 0xFF;
    other.format_ = 0xFF;
    other.data_ = 0;
    other.length_ = 0;
    other.val_byte_ = nullptr;
  }
  ~IFEntry() { delete_union(); }
  unsigned short tag() const { return tag_; }
  void tag(unsigned short tag) { tag_ = tag; }
  unsigned short format() const { return format_; }
  bool format(unsigned short format) {
    switch (format) {
      case 0x01:
      case 0x02:
      case 0x03:
      case 0x04:
      case 0x05:
      case 0x07:
      case 0x09:
      case 0x0a:
      case 0xff:
        break;
      default:
        return false;
    }
    delete_union();
    format_ = format;
    new_union();
    return true;
  }
  unsigned data() const { return data_; }
  void data(unsigned data) { data_ = data; }
  unsigned length() const { return length_; }
  void length(unsigned length) { length_ = length; }

  //访问数据的功能
  //
  //！！呼叫方有责任检查该格式！！
  //！！在访问其字段之前是正确的！！
  //-getter在这里用于将来添加
  //的检查格式是否正确
  byte_vector &val_byte() { return *val_byte_; }
  ascii_vector &val_string() { return *val_string_; }
  short_vector &val_short() { return *val_short_; }
  long_vector &val_long() { return *val_long_; }
  rational_vector &val_rational() { return *val_rational_; }

 private:
  // 原始字段
  unsigned short tag_;
  unsigned short format_;
  unsigned data_;
  unsigned length_;

  // 已分析字段
  union {
    byte_vector *val_byte_;
    ascii_vector *val_string_;
    short_vector *val_short_;
    long_vector *val_long_;
    rational_vector *val_rational_;
  };

  void delete_union() {
    switch (format_) {
      case 0x1:
        delete val_byte_;
        val_byte_ = nullptr;
        break;
      case 0x2:
        delete val_string_;
        val_string_ = nullptr;
        break;
      case 0x3:
        delete val_short_;
        val_short_ = nullptr;
        break;
      case 0x4:
        delete val_long_;
        val_long_ = nullptr;
        break;
      case 0x5:
        delete val_rational_;
        val_rational_ = nullptr;
        break;
      case 0xff:
        break;
      default:
        //不应该到这里
        //我应该抛出一个异常还是。。。？
        break;
    }
  }
  void new_union() {
    switch (format_) {
      case 0x1:
        val_byte_ = new byte_vector();
        break;
      case 0x2:
        val_string_ = new ascii_vector();
        break;
      case 0x3:
        val_short_ = new short_vector();
        break;
      case 0x4:
        val_long_ = new long_vector();
        break;
      case 0x5:
        val_rational_ = new rational_vector();
        break;
      case 0xff:
        break;
      default:
        //不应该到这里
        //我应该抛出一个异常还是。。。？
        break;
    }
  }
};

// 帮助函数
template <typename T, bool alignIntel>
T parse(const unsigned char *buf);

template <>
uint8_t parse<uint8_t, false>(const unsigned char *buf) {
  return *buf;
}

template <>
uint8_t parse<uint8_t, true>(const unsigned char *buf) {
  return *buf;
}

template <>
uint16_t parse<uint16_t, false>(const unsigned char *buf) {
  return (static_cast<uint16_t>(buf[0]) << 8) | buf[1];
}

template <>
uint16_t parse<uint16_t, true>(const unsigned char *buf) {
  return (static_cast<uint16_t>(buf[1]) << 8) | buf[0];
}

template <>
uint32_t parse<uint32_t, false>(const unsigned char *buf) {
  return (static_cast<uint32_t>(buf[0]) << 24) |
         (static_cast<uint32_t>(buf[1]) << 16) |
         (static_cast<uint32_t>(buf[2]) << 8) | buf[3];
}

template <>
uint32_t parse<uint32_t, true>(const unsigned char *buf) {
  return (static_cast<uint32_t>(buf[3]) << 24) |
         (static_cast<uint32_t>(buf[2]) << 16) |
         (static_cast<uint32_t>(buf[1]) << 8) | buf[0];
}

template <>
Rational parse<Rational, true>(const unsigned char *buf) {
  Rational r;
  r.numerator = parse<uint32_t, true>(buf);
  r.denominator = parse<uint32_t, true>(buf + 4);
  return r;
}

template <>
Rational parse<Rational, false>(const unsigned char *buf) {
  Rational r;
  r.numerator = parse<uint32_t, false>(buf);
  r.denominator = parse<uint32_t, false>(buf + 4);
  return r;
}

/**
 * 尝试读取此条目的entry.length()值
 *
 * 返回:
 *  true:已读取entry.length（）值
 *  false:出现问题，vec的内容未被触及
 */
template <typename T, bool alignIntel, typename C>
bool extract_values(C &container, const unsigned char *buf, const unsigned base,
                    const unsigned len, const IFEntry &entry) {
  const unsigned char *data;
  uint32_t reversed_data;

  //如果数据适合4个字节，则直接存储在IFEntry中的数据字段
  if (sizeof(T) * entry.length() <= 4) {
    if (alignIntel) {
      reversed_data = entry.data();
    } else {
      reversed_data = entry.data();
      // 这种逆转有效，但很难看
      unsigned char *rdata = reinterpret_cast<unsigned char *>(&reversed_data);
      unsigned char tmp;
      tmp = rdata[0];
      rdata[0] = rdata[3];
      rdata[3] = tmp;
      tmp = rdata[1];
      rdata[1] = rdata[2];
      rdata[2] = tmp;
    }
    data = reinterpret_cast<const unsigned char *>(&(reversed_data));
  } else {
    data = buf + base + entry.data();
    if (data + sizeof(T) * entry.length() > buf + len) {
      return false;
    }
  }
  container.resize(entry.length());
  for (size_t i = 0; i < entry.length(); ++i) {
    container[i] = parse<T, alignIntel>(data + sizeof(T) * i);
  }
  return true;
}

template <bool alignIntel>
void parseIFEntryHeader(const unsigned char *buf, unsigned short &tag,
                        unsigned short &format, unsigned &length,
                        unsigned &data) {
    //每个目录条目包括：
    //2字节：标记号（数据字段）
    //2字节：数据格式
    //4字节：组件数量
    //4字节：数据值或数据值偏移
  tag = parse<uint16_t, alignIntel>(buf);
  format = parse<uint16_t, alignIntel>(buf + 2);
  length = parse<uint32_t, alignIntel>(buf + 4);
  data = parse<uint32_t, alignIntel>(buf + 8);
}

template <bool alignIntel>
void parseIFEntryHeader(const unsigned char *buf, IFEntry &result) {
  unsigned short tag;
  unsigned short format;
  unsigned length;
  unsigned data;

  parseIFEntryHeader<alignIntel>(buf, tag, format, length, data);

  result.tag(tag);
  result.format(format);
  result.length(length);
  result.data(data);
}

template <bool alignIntel>
IFEntry parseIFEntry_temp(const unsigned char *buf, const unsigned offs,
                          const unsigned base, const unsigned len) {
  IFEntry result;

  //检查缓冲区中是否有足够的IFEntry数据
  if (buf + offs + 12 > buf + len) {
    result.tag(0xFF);
    return result;
  }

  parseIFEntryHeader<alignIntel>(buf + offs, result);

  // 以指定格式分析值
  switch (result.format()) {
    case 1:
      if (!extract_values<uint8_t, alignIntel>(result.val_byte(), buf, base,
                                               len, result)) {
        result.tag(0xFF);
      }
      break;
    case 2:
      // 字符串基本上是uint8_t的序列（好吧，根据EXIF偶数uint7_t，但是我们没有），所以只将其作为字节读取
      if (!extract_values<uint8_t, alignIntel>(result.val_string(), buf, base,
                                               len, result)) {
        result.tag(0xFF);
      }
      // 并在末尾剪切零字节，因为我们不希望这样
      if (result.val_string()[result.val_string().length() - 1] == '\0') {
        result.val_string().resize(result.val_string().length() - 1);
      }
      break;
    case 3:
      if (!extract_values<uint16_t, alignIntel>(result.val_short(), buf, base,
                                                len, result)) {
        result.tag(0xFF);
      }
      break;
    case 4:
      if (!extract_values<uint32_t, alignIntel>(result.val_long(), buf, base,
                                                len, result)) {
        result.tag(0xFF);
      }
      break;
    case 5:
      if (!extract_values<Rational, alignIntel>(result.val_rational(), buf,
                                                base, len, result)) {
        result.tag(0xFF);
      }
      break;
    case 7:
    case 9:
    case 10:
      break;
    default:
      result.tag(0xFF);
  }
  return result;
}

// 方便的辅助函数
template <typename T>
T parse_value(const unsigned char *buf, bool alignIntel) {
  if (alignIntel) {
    return parse<T, true>(buf);
  } else {
    return parse<T, false>(buf);
  }
}

void parseIFEntryHeader(const unsigned char *buf, bool alignIntel,
                        unsigned short &tag, unsigned short &format,
                        unsigned &length, unsigned &data) {
  if (alignIntel) {
    parseIFEntryHeader<true>(buf, tag, format, length, data);
  } else {
    parseIFEntryHeader<false>(buf, tag, format, length, data);
  }
}

IFEntry parseIFEntry(const unsigned char *buf, const unsigned offs,
                     const bool alignIntel, const unsigned base,
                     const unsigned len) {
  if (alignIntel) {
    return parseIFEntry_temp<true>(buf, offs, base, len);
  } else {
    return parseIFEntry_temp<false>(buf, offs, base, len);
  }
}

//
// 定位EXIF段并使用parseFromEXIFSegment对其进行解析
//
int EXIFInfo::parseFrom(const unsigned char *buf, unsigned len) {
  // Sanity check: all JPEG files start with 0xFFD8.
  if (!buf || len < 4) return PARSE_EXIF_ERROR_NO_JPEG;
  if (buf[0] != 0xFF || buf[1] != 0xD8) return PARSE_EXIF_ERROR_NO_JPEG;

  // 检查：一些相机在JPEG图像的末尾添加一些字节。
  //通常，我们应该能够在末尾找到JPEG结束标记0xFFD9
  //图像缓冲区的大小，但并不总是如此。只要有一些字节
  //除了图像缓冲器末尾的0xD9之外，保持递减len直到
  //则找到0xFFD9。如果没有找到JPEG结束标记0xFFD9，
  //那么我们可以合理地确定缓冲区不是JPEG。

  while (len > 2) {
    if (buf[len - 1] == 0xD9 && buf[len - 2] == 0xFF)
      break;
    len--;
  }
  if (len <= 2)
    return PARSE_EXIF_ERROR_NO_JPEG;

  clear();

  //扫描EXIF标头（字节0xFF 0xE1）并通过执行健全性检查
  //正在查找字节“Exif\0\0”。标记长度数据在摩托罗拉
  //字节顺序，这将导致parse16（）的'false'参数。
  //标记必须至少包含TIFF标头，否则
  //EXIF数据已损坏。因此，此处指定的最小长度必须为：
  //2字节：节大小
  //6字节：“Exif\0\0”字符串
  //2字节：TIFF标题（“II”或“MM”字符串）
  //2字节：TIFF魔术（摩托罗拉字节顺序中的短0x2a00）
  //4字节：偏移到第一个IFD
  //=========

  //  16 bytes
  unsigned offs = 0;  // 流入缓冲器的偏移
  for (offs = 0; offs < len - 1; offs++)
    if (buf[offs] == 0xFF && buf[offs + 1] == 0xE1) break;
  if (offs + 4 > len) return PARSE_EXIF_ERROR_NO_EXIF;
  offs += 2;
  unsigned short section_length = parse_value<uint16_t>(buf + offs, false);
  if (offs + section_length > len || section_length < 16)
    return PARSE_EXIF_ERROR_CORRUPT;
  offs += 2;

  return parseFromEXIFSegment(buf + offs, len - offs);
}

int EXIFInfo::parseFrom(const string &data) {
  return parseFrom(
      reinterpret_cast<const unsigned char *>(data.data()), static_cast<unsigned>(data.length()));
}

//
//EXIF段的主要解析函数。
//PARAM:EXIF TIFF的'buf'开头，必须是字节“EXIF\0\0”。
//PARAM:缓冲区的“len”长度
//
int EXIFInfo::parseFromEXIFSegment(const unsigned char *buf,
                                             unsigned len) {
  bool alignIntel = true;  // byte alignment (defined in EXIF header)
  unsigned offs = 0;       // current offset into buffer
  if (!buf || len < 6) return PARSE_EXIF_ERROR_NO_EXIF;

  if (!std::equal(buf, buf + 6, "Exif\0\0")) return PARSE_EXIF_ERROR_NO_EXIF;
  offs += 6;

  //现在正在解析TIFF标头。前两个字节是“II”或
  //“MM”表示英特尔或摩托罗拉的字节对齐。通过解析进行卫生检查
  //后面的无符号短，确保它等于0x2a。这个
  //最后4个字节是第一个IFD的偏移量，添加到
  //全局偏移计数器。对于这个块，我们期望如下
  //最小尺寸：
  //2字节：“II”或“MM”
  //2字节：0x002a
  //4字节：偏移到第一个IDF
  // -----------------------------
  //  8 bytes
  if (offs + 8 > len) return PARSE_EXIF_ERROR_CORRUPT;
  unsigned tiff_header_start = offs;
  if (buf[offs] == 'I' && buf[offs + 1] == 'I')
    alignIntel = true;
  else {
    if (buf[offs] == 'M' && buf[offs + 1] == 'M')
      alignIntel = false;
    else
      return PARSE_EXIF_ERROR_UNKNOWN_BYTEALIGN;
  }
  this->ByteAlign = alignIntel;
  offs += 2;
  if (0x2a != parse_value<uint16_t>(buf + offs, alignIntel))
    return PARSE_EXIF_ERROR_CORRUPT;
  offs += 2;
  unsigned first_ifd_offset = parse_value<uint32_t>(buf + offs, alignIntel);
  offs += first_ifd_offset - 4;
  if (offs >= len) return PARSE_EXIF_ERROR_CORRUPT;

  //现在解析第一个图像文件目录（IFD0，用于主图像）。
  //IFD由数量可变的12字节目录条目组成。这个
  //IFD部分的前两个字节包含目录的编号
  //部分中的条目。IFD的最后4个字节包含一个偏移
  //到下一个IFD，这意味着这个IFD必须恰好包含6+12*num字节的数据。
  if (offs + 2 > len) return PARSE_EXIF_ERROR_CORRUPT;
  int num_entries = parse_value<uint16_t>(buf + offs, alignIntel);
  if (offs + 6 + 12 * num_entries > len) return PARSE_EXIF_ERROR_CORRUPT;
  offs += 2;
  unsigned exif_sub_ifd_offset = len;
  unsigned gps_sub_ifd_offset = len;
  while (--num_entries >= 0) {
    IFEntry result =
        parseIFEntry(buf, offs, alignIntel, tiff_header_start, len);
    offs += 12;
    switch (result.tag()) {
      case 0x102:
        // Bits per sample
        if (result.format() == 3 && result.val_short().size())
          this->BitsPerSample = result.val_short().front();
        break;

      case 0x10E:
        // Image description
        if (result.format() == 2) this->ImageDescription = result.val_string();
        break;

      case 0x10F:
        // Digicam make
        if (result.format() == 2) this->Make = result.val_string();
        break;

      case 0x110:
        // Digicam model
        if (result.format() == 2) this->Model = result.val_string();
        break;

      case 0x112:
        // Orientation of image
        if (result.format() == 3 && result.val_short().size())
          this->Orientation = result.val_short().front();
        break;

      case 0x131:
        // Software used for image
        if (result.format() == 2) this->Software = result.val_string();
        break;

      case 0x132:
        // EXIF/TIFF date/time of image modification
        if (result.format() == 2) this->DateTime = result.val_string();
        break;

      case 0x8298:
        // Copyright information
        if (result.format() == 2) this->Copyright = result.val_string();
        break;

      case 0x8825:
        // GPS IFS offset
        gps_sub_ifd_offset = tiff_header_start + result.data();
        break;

      case 0x8769:
        // EXIF SubIFD offset
        exif_sub_ifd_offset = tiff_header_start + result.data();
        break;
    }
  }

  // 跳转到EXIF子IFD（如果存在）并解析所有信息
  //请注意，EXIF SubIFD可能不存在。
  //EXIF子IFD包含了典型的用户可能想要。
  if (exif_sub_ifd_offset + 4 <= len) {
    offs = exif_sub_ifd_offset;
    int num_sub_entries = parse_value<uint16_t>(buf + offs, alignIntel);
    if (offs + 6 + 12 * num_sub_entries > len) return PARSE_EXIF_ERROR_CORRUPT;
    offs += 2;
    while (--num_sub_entries >= 0) {
      IFEntry result =
          parseIFEntry(buf, offs, alignIntel, tiff_header_start, len);
      switch (result.tag()) {
        case 0x829a:
          // 曝光时间（秒）
          if (result.format() == 5 && result.val_rational().size())
            this->ExposureTime = result.val_rational().front();
          break;

        case 0x829d:
          // 光圈
          if (result.format() == 5 && result.val_rational().size())
            this->FNumber = result.val_rational().front();
          break;

      case 0x8822:
        // 曝光模式
        if (result.format() == 3 && result.val_short().size())
          this->ExposureProgram = result.val_short().front();
        break;

        case 0x8827:
          // ISO
          if (result.format() == 3 && result.val_short().size())
            this->ISOSpeedRatings = result.val_short().front();
          break;

        case 0x9003:
          // 原始日期和时间
          if (result.format() == 2)
            this->DateTimeOriginal = result.val_string();
          break;

        case 0x9004:
          //  数字化日期和时间
          if (result.format() == 2)
            this->DateTimeDigitized = result.val_string();
          break;

        case 0x9201:
          // 快门速度值
          if (result.format() == 5 && result.val_rational().size())
            this->ShutterSpeedValue = result.val_rational().front();
          break;

        case 0x9204:
          // 曝光EV值
          if (result.format() == 5 && result.val_rational().size())
            this->ExposureBiasValue = result.val_rational().front();
          break;

        case 0x9206:
          // 到焦点的距离（米）
          if (result.format() == 5 && result.val_rational().size())
            this->SubjectDistance = result.val_rational().front();
          break;

        case 0x9209:
          // 使用的闪光灯
          if (result.format() == 3 && result.val_short().size()) {
            uint16_t data = result.val_short().front();
            
            this->Flash = data & 1;
            this->FlashReturnedLight = (data & 6) >> 1;
            this->FlashMode = (data & 24) >> 3;
          }
          break;

        case 0x920a:
          // 焦距
          if (result.format() == 5 && result.val_rational().size())
            this->FocalLength = result.val_rational().front();
          break;

        case 0x9207:
          // 测光模式
          if (result.format() == 3 && result.val_short().size())
            this->MeteringMode = result.val_short().front();
          break;

        case 0x9291:
          // 拍摄原始照片的子秒（日期的秒）
          if (result.format() == 2)
            this->SubSecTimeOriginal = result.val_string();
          break;

        case 0xa002:
          // EXIF图像宽度
          if (result.format() == 4 && result.val_long().size())
            this->ImageWidth = result.val_long().front();
          if (result.format() == 3 && result.val_short().size())
            this->ImageWidth = result.val_short().front();
          break;

        case 0xa003:
          // EXIF图像高度
          if (result.format() == 4 && result.val_long().size())
            this->ImageHeight = result.val_long().front();
          if (result.format() == 3 && result.val_short().size())
            this->ImageHeight = result.val_short().front();
          break;

        case 0xa20e:
          // EXIF焦平面X分辨率
          if (result.format() == 5) {
            this->LensInfo.FocalPlaneXResolution = result.val_rational()[0];
          }
          break;

        case 0xa20f:
          // EXIF焦平面Y分辨率
          if (result.format() == 5) {
            this->LensInfo.FocalPlaneYResolution = result.val_rational()[0];
          }
          break;

        case 0xa210:
            // EXIF焦平面分辨率单元
            if (result.format() == 3 && result.val_short().size()) {
                this->LensInfo.FocalPlaneResolutionUnit = result.val_short().front();
            }
            break;

        case 0xa405:
          // 等效35mm焦距
          if (result.format() == 3 && result.val_short().size())
            this->FocalLengthIn35mm = result.val_short().front();
          break;

        case 0xa432:
          // 焦距和光圈
          if (result.format() == 5) {
            int sz = static_cast<unsigned>(result.val_rational().size());
            if (sz)
              this->LensInfo.FocalLengthMin = result.val_rational()[0];
            if (sz > 1)
              this->LensInfo.FocalLengthMax = result.val_rational()[1];
            if (sz > 2)
              this->LensInfo.FStopMin = result.val_rational()[2];
            if (sz > 3)
              this->LensInfo.FStopMax = result.val_rational()[3];
          }
          break;

        case 0xa433:
          // 镜头制造商.
          if (result.format() == 2) {
            this->LensInfo.Make = result.val_string();
          }
          break;

        case 0xa434:
          // 镜头型号.
          if (result.format() == 2) {
            this->LensInfo.Model = result.val_string();
          }
          break;
      }
      offs += 12;
    }
  }

  // 如果存在，则跳转到GPS SubIFD并解析所有信息 GPS SubIFD可能不存在。
  if (gps_sub_ifd_offset + 4 <= len) {
    offs = gps_sub_ifd_offset;
    int num_sub_entries = parse_value<uint16_t>(buf + offs, alignIntel);
    if (offs + 6 + 12 * num_sub_entries > len) return PARSE_EXIF_ERROR_CORRUPT;
    offs += 2;
    while (--num_sub_entries >= 0) {
      unsigned short tag, format;
      unsigned length, data;
      parseIFEntryHeader(buf + offs, alignIntel, tag, format, length, data);
      switch (tag) {
        case 1:
          // GPS北或南
          this->GeoLocation.LatComponents.direction = *(buf + offs + 8);
          if (this->GeoLocation.LatComponents.direction == 0) {
            this->GeoLocation.LatComponents.direction = '?';
          }
          if ('S' == this->GeoLocation.LatComponents.direction) {
            this->GeoLocation.Latitude = -this->GeoLocation.Latitude;
          }
          break;

        case 2:
          // GPS纬度
          if ((format == 5 || format == 10) && length == 3) {
            this->GeoLocation.LatComponents.degrees = parse_value<Rational>(
                buf + data + tiff_header_start, alignIntel);
            this->GeoLocation.LatComponents.minutes = parse_value<Rational>(
                buf + data + tiff_header_start + 8, alignIntel);
            this->GeoLocation.LatComponents.seconds = parse_value<Rational>(
                buf + data + tiff_header_start + 16, alignIntel);
            this->GeoLocation.Latitude =
                this->GeoLocation.LatComponents.degrees +
                this->GeoLocation.LatComponents.minutes / 60 +
                this->GeoLocation.LatComponents.seconds / 3600;
            if ('S' == this->GeoLocation.LatComponents.direction) {
              this->GeoLocation.Latitude = -this->GeoLocation.Latitude;
            }
          }
          break;

        case 3:
          // GPS东或西
          this->GeoLocation.LonComponents.direction = *(buf + offs + 8);
          if (this->GeoLocation.LonComponents.direction == 0) {
            this->GeoLocation.LonComponents.direction = '?';
          }
          if ('W' == this->GeoLocation.LonComponents.direction) {
            this->GeoLocation.Longitude = -this->GeoLocation.Longitude;
          }
          break;

        case 4:
          // GPS经度
          if ((format == 5 || format == 10) && length == 3) {
            this->GeoLocation.LonComponents.degrees = parse_value<Rational>(
                buf + data + tiff_header_start, alignIntel);
            this->GeoLocation.LonComponents.minutes = parse_value<Rational>(
                buf + data + tiff_header_start + 8, alignIntel);
            this->GeoLocation.LonComponents.seconds = parse_value<Rational>(
                buf + data + tiff_header_start + 16, alignIntel);
            this->GeoLocation.Longitude =
                this->GeoLocation.LonComponents.degrees +
                this->GeoLocation.LonComponents.minutes / 60 +
                this->GeoLocation.LonComponents.seconds / 3600;
            if ('W' == this->GeoLocation.LonComponents.direction)
              this->GeoLocation.Longitude = -this->GeoLocation.Longitude;
          }
          break;

        case 5:
          // GPS高度参考（低于或高于海平面）
          this->GeoLocation.AltitudeRef = *(buf + offs + 8);
          if (1 == this->GeoLocation.AltitudeRef) {
            this->GeoLocation.Altitude = -this->GeoLocation.Altitude;
          }
          break;

        case 6:
          // GPS高度
          if ((format == 5 || format == 10)) {
            this->GeoLocation.Altitude = parse_value<Rational>(
                buf + data + tiff_header_start, alignIntel);
            if (1 == this->GeoLocation.AltitudeRef) {
              this->GeoLocation.Altitude = -this->GeoLocation.Altitude;
            }
          }
          break;

        case 11:
          // GPS精度（DOP）
          if ((format == 5 || format == 10)) {
            this->GeoLocation.DOP = parse_value<Rational>(
                buf + data + tiff_header_start, alignIntel);
          }
          break;
      }
      offs += 12;
    }
  }

  return PARSE_EXIF_SUCCESS;
}

void EXIFInfo::clear() {
  // Strings
  ImageDescription = "";
  Make = "";
  Model = "";
  Software = "";
  DateTime = "";
  DateTimeOriginal = "";
  DateTimeDigitized = "";
  SubSecTimeOriginal = "";
  Copyright = "";

  // Shorts / unsigned / double
  ByteAlign = 0;
  Orientation = 0;

  BitsPerSample = 0;
  ExposureTime = 0;
  FNumber = 0;
  ExposureProgram = 0;
  ISOSpeedRatings = 0;
  ShutterSpeedValue = 0;
  ExposureBiasValue = 0;
  SubjectDistance = 0;
  FocalLength = 0;
  FocalLengthIn35mm = 0;
  Flash = 0;
  FlashReturnedLight = 0;
  FlashMode = 0;
  MeteringMode = 0;
  ImageWidth = 0;
  ImageHeight = 0;

  // 地理位置
  GeoLocation.Latitude = 0;
  GeoLocation.Longitude = 0;
  GeoLocation.Altitude = 0;
  GeoLocation.AltitudeRef = 0;
  GeoLocation.DOP = 0;
  GeoLocation.LatComponents.degrees = 0;
  GeoLocation.LatComponents.minutes = 0;
  GeoLocation.LatComponents.seconds = 0;
  GeoLocation.LatComponents.direction = '?';
  GeoLocation.LonComponents.degrees = 0;
  GeoLocation.LonComponents.minutes = 0;
  GeoLocation.LonComponents.seconds = 0;
  GeoLocation.LonComponents.direction = '?';

  // 镜头信息
  LensInfo.FocalLengthMax = 0;
  LensInfo.FocalLengthMin = 0;
  LensInfo.FStopMax = 0;
  LensInfo.FStopMin = 0;
  LensInfo.FocalPlaneYResolution = 0;
  LensInfo.FocalPlaneXResolution = 0;
  LensInfo.FocalPlaneResolutionUnit = 0;
  LensInfo.Make = "";
  LensInfo.Model = "";
}
